<template>
  <teleport to="body"
    ><template v-if="isRender"
      ><transition name="dialog-mark"
        ><div class="dialog" v-show="showDialog" :class="{ isMark: showMark }">
          <div class="dialog-box flex">
            <transition name="dialog-content">
              <div
                ref="dialogMain"
                class="dialog-main"
                v-draggable="{ draggable }"
                v-show="showDialog"
                :style="({ '--dialogWid': dialogWid } as any)"
              >
                <header
                  id="header"
                  :class="{ 'dialog-header': true, 'dialog-header__move': draggable }"
                >
                  <span v-if="title">{{ title }}</span>
                  <svg-icon
                    v-if="showClose"
                    icon-class="close"
                    popper-class="dialog-close"
                    @click="closeModal"
                  ></svg-icon>
                </header>
                <div class="dialog-body"><slot></slot></div>
                <div class="dialog-footer"><slot name="footer"></slot></div></div
            ></transition>
          </div></div></transition></template
  ></teleport>
</template>

<script lang="ts" setup>
import { ref, watch, nextTick, onBeforeUnmount, onMounted, watchEffect } from "vue";
import { getScrollWid } from "@/plugins/getScrollWid";
import vDraggable from "@/directives/draggable";

const props = withDefaults(
  defineProps<{
    // 是否该显示对话框
    modelValue: boolean;
    // 是否需要显示遮罩层
    showMark?: boolean;
    // 是否显示关闭按钮
    showClose?: boolean;
    // 对话框的标题
    title?: string;
    // 对话框的宽度
    width?: number | string;
    // 对话框是否可拖动
    draggable?: boolean;
    // 是否可以点击mark将对话框关闭
    markClose?: boolean;
    // 是否可以通过点击关闭图标来关闭对话框
    iconClose?: boolean;
  }>(),
  {
    showMark: true,
    showClose: true,
    title: "",
    width: "50%",
    draggable: false,
    markClose: true,
    iconClose: true,
  }
);
const emits = defineEmits<{
  (e: "update:modelValue", show: boolean): void;
}>();

/**
 * 对话框的宽度
 */
const dialogWid = ref<string>("30%");

watchEffect(() => {
  if (props.width) {
    if (!isNaN(+props.width)) dialogWid.value = `${props.width}px`;
    else if (
      typeof props.width === "string" &&
      (/^\d+%$/.test(props.width) || /^\d+px$/.test(props.width))
    )
      dialogWid.value = props.width;
  }
});

/**
 * 是否关闭该模态框
 */
let showDialog = ref<boolean>(false);
/**
 * 是否渲染此组件
 */
let isRender = ref<boolean>(false);

/**
 * 浏览器的可视高度改变则组件的根元素的高度也随之改变
 */
const handleToWid: () => void = (): void => {
  // 获取浏览器的可视高度
  const hei: number =
    globalThis.innerHeight ||
    document.body.clientHeight ||
    document.documentElement.clientHeight;
  // 获取浏览器的可视宽度
  const wid: number =
    globalThis.innerWidth ||
    document.body.clientWidth ||
    document.documentElement.clientWidth;
  if (document.body.scrollHeight > hei || document.body.scrollWidth > wid)
    document.body.style.cssText += `overflow: hidden;padding-right: ${getScrollWid()}px`;
  else if (document.body.scrollHeight <= hei || document.body.scrollWidth <= wid)
    document.body.style.cssText = "";
};

/**
 * 对话框元素
 */
const dialogMain = ref<HTMLDivElement>();
/**
 * 判断用户所点击的元素是否符合标准
 */
const judgeClick = (e: Event) => {
  const target: HTMLElement = e.target as HTMLElement;
  if (!dialogMain.value?.contains(target)) emits("update:modelValue", false);
};

watch(
  (): boolean => props.modelValue,
  (show: boolean): void => {
    if (show) {
      // 给浏览器添加监听窗口大小变化的事件
      window.addEventListener("resize", handleToWid);
      isRender.value = true;
      nextTick(() => {
        showDialog.value = true;
        handleToWid();
        setTimeout(() => {
          if (props.markClose) document.addEventListener("click", judgeClick);
        });
      });
    } else {
      window.removeEventListener("resize", handleToWid);
      document.body.style.cssText = "";
      dialogMain.value!.style.transform = "";
      showDialog.value = false;
      setTimeout(() => (isRender.value = false), 300);
      if (props.markClose) document.removeEventListener("click", judgeClick);
    }
  }
);

onMounted(() => {
  if (props.modelValue) {
    isRender.value = true;
    window.addEventListener("resize", handleToWid);
    setTimeout(() => {
      showDialog.value = true;
      handleToWid();
    }, 0);
  }
});

onBeforeUnmount(() => window.removeEventListener("resize", handleToWid));

// 关闭该对话框
const closeModal: () => void = (): void =>
  props.iconClose ? emits("update:modelValue", false) : undefined;
</script>

<style lang="scss" scoped>
.isMark {
  background-color: rgba(0, 0, 0, 0.5);
}

.dialog {
  &-mark {
    @include fade(0.25s, ease);
  }

  &-content {
    &-enter-active,
    &-leave-active {
      transition: transform 0.3s;
    }

    &-enter-from,
    &-leave-to {
      transform: translateY(-30px);
    }
  }
}
.dialog {
  position: fixed;
  inset: 0;
  z-index: 2000;
  overflow: auto;

  &-box {
    position: relative;
    min-height: 550px;
    @include setWidHei(100%, 100%);
  }

  &-header {
    padding: 20px 25px 10px;
    position: relative;
    text-align: center;
    word-break: break-all;
    font-size: 18px;

    &__move {
      cursor: move;
    }
  }

  &-body {
    padding: 10px 25px;
    word-break: break-all;
    color: #606266;
    font-size: 14px;
  }

  &-footer {
    padding: 20px 25px;
    text-align: center;
    word-break: break-all;
  }

  &-main {
    position: relative;
    width: var(--dialogWid);
    box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.04), 0px 8px 20px rgba(0, 0, 0, 0.08);
    border-radius: 3px;
    background-color: #fff;
  }

  &-close {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    right: 15px;
    color: #909399;
    cursor: pointer;
    transition: color 0.2s;

    &:hover {
      color: #79bbff;
    }
  }
}
</style>
